#include <assert.h>
#include <time.h>
#include <wlr/interfaces/wlr_pointer.h>
#include "backend/sylixosinput.h"
#include "util/signal.h"

const struct wlr_pointer_impl sylixosinput_pointer_impl = {
	.name = "sylixosinput-pointer",
};

void init_device_pointer(struct wlr_sylixosinput_input_device *dev) {
	struct wlr_pointer *wlr_pointer = &dev->pointer;
	wlr_pointer_init(wlr_pointer, &sylixosinput_pointer_impl, "xmse");
	wlr_pointer->base.vendor = 0xdead;
	wlr_pointer->base.product = 0xdead;
}

struct wlr_sylixosinput_input_device *device_from_pointer(
		struct wlr_pointer *wlr_pointer) {
	assert(wlr_pointer->impl == &sylixosinput_pointer_impl);

	struct wlr_sylixosinput_input_device *dev =
		wl_container_of(wlr_pointer, dev, pointer);
	return dev;
}

void handle_pointer_motion(mouse_event_notify *event, struct wlr_pointer *pointer) {
	struct wlr_pointer_motion_event wlr_event = { 0 };
    struct timeval time_us;

    gettimeofday(&time_us, NULL);
	wlr_event.pointer = pointer;
	wlr_event.time_msec = usec_to_msec(time_us.tv_sec * 1000 * 1000 + time_us.tv_usec);
	wlr_event.delta_x = event->xmovement;
	wlr_event.delta_y = event->ymovement;
	wlr_event.unaccel_dx = event->xmovement;
	wlr_event.unaccel_dy = event->ymovement;
	wlr_signal_emit_safe(&pointer->events.motion, &wlr_event);
	wlr_signal_emit_safe(&pointer->events.frame, pointer);
}

void handle_pointer_motion_abs(mouse_event_notify *event, struct wlr_pointer *pointer) {
	struct wlr_pointer_motion_absolute_event wlr_event = { 0 };
    struct timeval time_us;

    gettimeofday(&time_us, NULL);
	wlr_event.pointer = pointer;
	wlr_event.time_msec = usec_to_msec(time_us.tv_sec * 1000 * 1000 + time_us.tv_usec);
	wlr_event.x = event->xmovement;
	wlr_event.y = event->ymovement;
	wlr_signal_emit_safe(&pointer->events.motion_absolute, &wlr_event);
	wlr_signal_emit_safe(&pointer->events.frame, pointer);
}

void handle_pointer_button(mouse_event_notify *event, struct wlr_pointer *pointer) {
	struct wlr_pointer_button_event wlr_event = { 0 };
    struct timeval time_us;
    static int mouse_left_button_state = 0;
    static int mouse_right_button_state = 0;
    static int button = 0;
    static int button_state = 0;

    // get pointer button status
    if (event->kstat) {
        button_state = WL_POINTER_BUTTON_STATE_PRESSED;

        if (event->kstat & MOUSE_LEFT) {
            button = MOUSE_LEFT;
            mouse_left_button_state = 1;
        } else if (event->kstat & MOUSE_RIGHT) {
            button = MOUSE_RIGHT;
            mouse_right_button_state = 1;
        } else {
            button = MOUSE_MIDDLE;
        }
    } else {
        button_state = WL_POINTER_BUTTON_STATE_RELEASED;

        if (!(event->kstat & MOUSE_LEFT) && (mouse_left_button_state == 1)) {
            button = MOUSE_LEFT;
            mouse_left_button_state = 0;
        } else if (!(event->kstat & MOUSE_RIGHT) && (mouse_right_button_state == 1)) {
            button = MOUSE_RIGHT;
            mouse_right_button_state = 0;
        } else {
            button = MOUSE_MIDDLE;
        }
    }

    gettimeofday(&time_us, NULL);
	wlr_event.pointer = pointer;
	wlr_event.time_msec = usec_to_msec(time_us.tv_sec * 1000 * 1000 + time_us.tv_usec);
	wlr_event.button = button;

	switch (button_state) {
	case WL_POINTER_BUTTON_STATE_PRESSED:
		wlr_event.state = WLR_BUTTON_PRESSED;
		break;
	case WL_POINTER_BUTTON_STATE_RELEASED:
		wlr_event.state = WLR_BUTTON_RELEASED;
		break;
	}
	wlr_signal_emit_safe(&pointer->events.button, &wlr_event);
	wlr_signal_emit_safe(&pointer->events.frame, pointer);
}

void handle_pointer_axis(mouse_event_notify *event, struct wlr_pointer *pointer) {
	struct wlr_pointer_axis_event wlr_event = { 0 };
	struct timeval time_us;

	gettimeofday(&time_us, NULL);
	wlr_event.pointer = pointer;
	wlr_event.time_msec = usec_to_msec(time_us.tv_sec * 1000 * 1000 + time_us.tv_usec);
    wlr_event.source = WLR_AXIS_SOURCE_WHEEL;
    wlr_event.orientation = WLR_AXIS_ORIENTATION_VERTICAL;
    wlr_event.delta = -(event->wscroll[0] * 120) / 12;
    wlr_event.delta_discrete = 0;
    wlr_signal_emit_safe(&pointer->events.axis, &wlr_event);
	wlr_signal_emit_safe(&pointer->events.frame, pointer);
}
